Java基础(十一)——输入输出

注意try块的使用:由于finally语句较臃肿,Java7允许try关键字后面跟一对圆括号,括号中声明或初始化那些必须在程序结束时显式关闭的资源。这些资源必须实现AutoCloseable接口或其子接口Closeable接口。
AutoCloseable接口里的close()方法声明抛出了Exception,可以抛出任何异常;而其子接口Closeable接口里的close()方法声明抛出了IOException,只能抛出IOException及其子类。
工厂类、工具类(factory class)

Java的IO通过java.io包下的类和接口来支持,主要包括输入、输出两种IO流,每种输入、输出流又可分为字节流和字符流两大类。IO流使用了一种装饰器设计模式,它将IO流分成底层节点流和上层处理流。
java.nio及其子包下提供了一系列全新的API,也称NIO 2。
Java对象的序列化机制,可以报内存中Java对象转换成二进制字节流,这样就可以把Java对象存储到磁盘里,或在网络上传输Java对象。

File类

访问文件和目录

文件过滤器

File类的list()方法可以接收一个FilenameFile参数,可以只列出符合条件的文件。

IO流

java.io包中。
Java把不同的输入/输出源(如键盘、文件、网络连接等)抽象描述为流(stream)。
输入流主要由InputStream和Reader作为基类,输出流主要由OutputStream和Writer作为基类。
字节流(数据单位是字节(byte,8bit))主要由InputStream和OutputSteam作为基类,字符流(数据单位是字符(char,两个字节(16bit)))主要由Reader和Writer作为基类。
InputStream和Reader、OutputStream和Writer都是抽象类,无法直接创建实例。但它们分别有一个读取文件输入、输出流。以InputStream和Reader为例,用于读取文件的输入流是FileInputStream和FileReader,它们都是节点流,直接和指定文件关联。
从输入流中读取数据,抽象为使用一个竹筒到水管中取水。每个中文字符占两个字节。

1
2
3
4
5
6
7
8
9
FileInputStream fis = new FileInputStream(“a.java”); //创建字节输入流
byte[] bbf = new byte[1024]; //创建数组(竹筒),保存从输入流中读取的数据
int hasRead = 0; //保存实际读取的字节数
while((hasRead = fis.read(bbf)) > 0){ //读取文件的字节流,并保存在bbf数组中
new String(bbf, 0, hasRead); //将字节数组转换成String字符串,byte 1字节->char 2字节
}
finally{
fis.close();
}

其中FileInputStream中的int read(byte[] b)方法:Reads up to b.length bytes of data from this input stream into an array of bytes. Return total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached
与JDBC编程一样,程序里打开的IO资源不属于内存里的资源,垃圾回收机制无法回收它们,所以应当显式关闭打开的IO资源
Java7改写了所有的IO资源类,它们都实现了AutoCloseable接口,因此都可以通过自动关闭资源的try语句来关闭这些IO流。

输入输出流体系

上面介绍了4个基类访问文件的节点流的用法,使用起来繁琐,可借助处理流简化编程。
处理流的功能:使用处理流来包装节点流,,隐藏底层设备上节点流的差异,并对外提供更方便的输入/输出方法,让我们只关注高级流的操作。
节点流:
节点流
处理流:
处理流

处理流的用法

只需要在创建处理流时传入一个节点流作为构造参数即可。
下面使用PrintStream处理流来包装OutputStream:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PrintStreamTest{
public static void main(String[] args){
try(
//创建文件字节输出流
FileOutputStream fos = new FileOutputStream(“test.txt”);
PrintStream ps = new PrintStream(fos)){
ps.println(“普通字符串”); //使用PrintStream输出字符串
ps.println(new PrintStreamTest()); //使用PrintStream输出对象
} catch(IOException ioe){
ioe.printStackTrace();
}
}
}

标准输出(Sytem.out)的类型就是PrintStream。
通常如果需要输出文本内容,都应该将输出流包装成PrintStream后进行输出

输入/输出流体系

通常有一个规则:如果进行输入/输出的内容是二进制内容,应该考虑使用字节(byte)流;如果进行输入/输出的内容是文本内容,则应该考虑使用字符(char)流

转换流

将字节流转换成字符流:InputStreamReader,OutputStreamWriter
System.in代表标准输入,即键盘输入,但这个标准输入流是InputStream类(字节流)的实例,使用不太方便,而且键盘输入的都是文本内容,所以可以使用InputStreamReader将其转换成字符输入流。将普通Reader包装成BufferedReader(处理流),利用readLiner()方法可以一次读取一行的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class KeyinTest{
public static void main(String[] args){
//将System.in对象转换成Reader对象
try(InputStreamReader reader = new InputStreamReader(System.in);
//将普通Reader包装成BufferedReader
BufferedReader br = new BufferedReader(reader)){
String line = null;
//采用循环方式逐行读取
while((line = br.readLine()) != null){
if(line.equals(“exit”);
System.exi(1); //非正常退出程序
System.out.println(“输入内容为:” + line);
}
} catch(IOException ioe){
ioe.printStackTrace();
}
}
}

BufferedReader具有缓冲功能,它可以逐行读取文本。以换行符为标识,如果没有读到换行符,则程序阻塞,等到读到换行符为止。
注意:
System.exit(int status)这个方法是用来结束当前正在运行中的java虚拟机。
System.exit(0)是正常退出程序,而System.exit(1)或者说非0表示非正常退出程序。

推回输入流

PushbackInputStream,PushbackReader。
这两个推回输入流都带有一个推回缓冲区,当程序调用两个推回输入流的unread()方法时,系统会把指定数组的内容退回到该缓冲区里。

重定向输入/输出流

Java的标准输入/输出通过System.in和System.out来代表,在默认情况下分别代表键盘和显示器。
//创建PrintStream(打印)输出流,将节点流包装成处理流
PrintStream ps = new PrintStream(new FileOutputStream(“out.tex”));
//将标准输出重定向到ps输出流
System.setOut(ps);

JVM读写其它进程的数据

使用Runtime对象的exec()方法课可以运行平台上的其它程序,该方法产生一个Process对象,代表由该java程序启动的子进程。
让程序和其进程进行通信:

1
2
3
4
5
6
7
8
9
10
11
//运行javac命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec(“javac”);
//以p进程的错误流创建BufferReader对象
try(
BufferedReader br = new BufferedReader(new
InputStreamReader(p.getErrorStream()))){
String buff = null;
while((buff = br.readLine()) != null){
System.out.println(buff);
}
};

上面程序分析:
p.getErrorStream():p进程的标准错误流输入
InputStreamReader:标准输入是文本输入,将文本转换成字符流
BufferedReader:将普通Reader包装成BufferedReader,利用readLiner()方法可以一次读取一行的内容。
衡量输入输出总是站在本程序的角度

RandomAccessFile

它与普通的输入/输出流不同的是,RadomAccessFile支持随机(任意更贴切)访问方式,程序可以直接跳转到文件的任意地方来读写数据(允许文件自由地移动文件指针,任意访问文件的制定位置)。因此对于只访问文件的部分内容,使用RandomAccessFile更好。
如果程序需要向已存在的文件中追加内容,则应该使用RandomAccessFile。
局限:只能读写文件,不能读写IO节点
创建RandomAccessFile对象需要制定mode参数:
(1)“r”:只读方式打开文件
(2)“rw”:读、写
(3)“rws”:相比于rw模式,对文件内容或元数据的每个更新都要同步写入底层存储设备。
(4)“rwd”:相比于rw模式,对文件内容的每个更新都要同步写入底层存储设备。
RandomAccessFile不能直接向文件中的指定位置添加内容,否则会覆盖原文件中的内容,需要先把插入点后面的内容放入缓冲区,等把需要的内容写入文件后,再将缓冲区中内容追加到文件后面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.io.RandomAccessFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
*向指定文件、指定位置插入内容
*/
public class InsertContent{
//定义insert()方法:文件名,插入点位置,插入内容
public static void insert(String fileName
, long pos, String insertContent) throws IOException{
File tmp = File.createTempFile("tmp", null);//创建临时文件
tmp.deleteOnExit();//临时文件将在JVM退出时删除

try(
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
//使用临时文件来保存插入点后的数据
//创建tmp的字节输出流临时文件
FileOutputStream tmpOut = new FileOutputStream(tmp);
FileInputStream tmpIn = new FileInputStream(tmp);){
raf.seek(pos);
//----将插入点之后的内容读入到临时文件中保存-----//
byte[] bbuf = new byte[64];
int hasRead = 0;
//读取raf文件对象中的内容,并放在bbuf字节类型数组中
while((hasRead = raf.read(bbuf)) > 0){
tmpOut.write(bbuf, 0, hasRead);
}
//----插入内容----//
raf.seek(pos);
raf.write(insertContent.getBytes());
//----追加临时文件中的内容----//
while((hasRead = tmpIn.read(bbuf)) > 0){
raf.write(bbuf, 0, hasRead);
}
}
catch(IOException ioe){ioe.printStackTrace();}
}
public static void main(String[] args)
throws Exception {
insert("F:\\eclipsework\\Crazy-java\\src\\com\\Licht\\_15\\InsertContent.java"
, 45, "插入的内容\r\n");
}
}

对象序列化(Serialize)

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流(字节序列),将对象保存到磁盘中,或允许在网络中直接传输对象。
对象的序列化(Serialize)指将一个Java对象写入IO流;对象的反序列化(Deserialize)则指从IO流中恢复该Java对象。
这个对象必须是可序列化的(serializable),它的类必须实现两个接口之一:
(1) Serializable 该接口是一个标记接口,只表明该类的实例是可序列化的,实现该接口无法实现任何方法。
(2) Externalizable(可外部化的)
所有可能在网络上传输的对象都应该是可序列化的。比如RMI(Remote Method Invoke,远程方法调用,是Java EE的基础)过程中的参数和返回值。

使用对象流实现序列化

通常建议:程序创建的每个JavaBean类都实现Serializable。
1) 某个类实现Serializable接口;

1
class Person implements java.io.Serializable{}

2) 创建一个ObjectOutputStream,这个输出流是一个处理流,所以必须建立在其它节点流的基础上;

1
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“object.txt”));

3) 调用ObjectOutputStream对象的writeObject()方法输出可序列化对象;

1
2
Person per = new Person(“悟空”, 500);
oos.writeObject(per); //将per对象转换成字节序列并输出

如果希望从二进制中恢复Java对象,需要反序列化。
4) 创建一个ObjectInputStream,这个输入流是一个处理流,所以必须建立在其它节点流的基础上;

1
ObjectInputStream oos = new ObjectInputStream(new FileInputStream(“object.txt”));

5) 调用ObjectInputStream对象的readObject()方法读取流中的对象;

1
Person per = (Person)ois.readObject(); //从输入流中读取一个对象,并强制转换成Person

反序列化读取的知识Java对象的数据,而不是Java类,因此采用反序列化恢复Java对象时,必须提供Java对象所属类的class文件

对象引用的序列化

前面的Person类的两个成员变量分别是String类型和int类型,如果某个成员变量不是String或基本类型,而是引用类型,那么这个引用类必须是可序列化的。
如程序序列化一个Teacher对象时,该对象持有一个Person类对象的引用,为了在反序列化时可以正常恢复该Teacher对象,程序会顺带将Person对象也进行序列化,所以Person类也必须是可序列化的。
为了防止多次重复序列化一个对象,Java序列化机制采用了一种特殊的序列化算法:
(1) 所有保存到磁盘中的对象都有一个序列化编号
(2) 当程序试图序列化一个对象时,程序会先检查该对象是否已经被序列化,如果在本次虚拟机中没有被序列化过,系统才会将该对象转换成字节序列并输出。
(3) 如果已经被序列化过,则程序直接输出一个序列化编号。
但这样也会出现一个问题:当程序再次调用writeObject()方法时,程序只是输出前面的序列化编号,即使后面该对象的实例变量值已经改变,改变的实例变量值也不会输出。所以对于可变对象一定要注意。

自定义序列化

当对某个对象进行序列化时,系统会自动把该对象的所有实例变量依次序列化,如果某个实例变量引用到另一个对象,则被引用对象也会被序列化。这种序列化会一直持续下去,称为递归序列化。
但在一些特殊场景下,不希望将实例变量序列化,或者某个实例变量的类型不应该实例化,则在不需要序列化的实例变量前加transient关键字(短暂的,瞬时的)。
但这样也出现一个问题:被transient修饰的实例变量将完全隔离在序列化机制之外,导致使用反序列化恢复对象时,无法获得该实例变量的值。

另一种自定义序列化机制

完全有我们决定存储和恢复对象数据,必须实现Externalizable接口。

版本

反序列化对象时需要提供该对象的class文件,如何保证两个class文件兼容?若不指定serialVersionUID类变量,不利于程序在不同JVM之间移植,因为不同编译器对该类变量的计算策略可能不同。
解决:为每个要序列化的类中添加private static final serialVersionUID这个类变量,具体数值自己定义。

NIO(New IO)

传统输入/输出是基于阻塞式的,通过字节移动来处理,即面向流的输入/输出系统一次只能处理一个字节,效率不高。
NIO采用内存映射的方式,将文件或文件的一段区域映射到内存,这样就可以像访问内存一样访问文件了。
Channel(通道)和Buffer(缓冲)是NIO中两个核心对象。Channel是对传统输入/输出的模拟,不同之处是增加了map()方法进行数据映射,将Channel对应的部分或全部数据映射成Buffer。Buffer是一个容器,本质是一个数组(类似于前面的“竹筒”)。程序不能直接访问Channel中的数据,Channel的读取必须通过Buffer交互。
InuputStream/OutputStream<->Channel<-(map())->Buffer

使用Buffer

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
Buffer是一个抽象类,常用子类是ByteBuffer、CharBuffer。它没有构造器,通过static Buffer allocate(int capacity) 来创建对象。
Buffer读入数据后的示意图
Buffer装入数据后,调用Buffer的flip()方法将limit设为position,目的是将没有数据的区域封印起来,避免程序从Buffer中去除null值。

使用Channel

import java.nio.channels.FileChannel;

Java为Channel接口提供了DatagramChannel(UDP网络通信的Channel)、FileChannel、Pipe.SinkChannel、Pipe.SourceChannel(支持线程之间通信的管道Channel)、SelectableChannel、ServerSocketChannel、SoceketChannel(支持TCP网络通信的Channel)等实现类。
所有的Channel都不应该使用构造器来直接创建,而是通过传统的节点InputStream和OutputStream的getChannel()方法来返回对应的channel。
map()方法的方法签名为:MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
/*
*直接将FileChannel的全部数据映射成ByteBuffer
*然后通过FileChannel输出
*/
public class FileChannelTest{
public static void main(String[] args){
File f = new File("F:\\eclipsework\\Crazy-java\\src\\com\\Licht\\_15\\FileChannelTest.java");
try(FileChannel inChannel = new FileInputStream(f).getChannel();
FileChannel outChannel = new FileOutputStream(f).getChannel();){
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
//将ByreBuffer中的数据写入FileChannel中输出
outChannel.write(buffer);
buffer.clear(); // 复原position、limit的位置
/*---为了能打印文件,使用的Charset类和CharsetDecoder类
将ByteBuffer转换成CharBuffer---*/
//使用forName(字符集名称)创建GBK字符集对象
Charset charset = Charset.forName("GBK");
//创建解码器对象,使用解码器将ByteBuffer转换成CharBuffer
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buffer);
System.out.println(charBuffer);
}
catch(IOException ioe){ioe.printStackTrace();}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
* 利用Buffer和Channel传统方式,
* 不使用map()映射
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
public class ReadFile{
public static void main(String[] args)
throws IOException{
try(FileInputStream fis = new FileInputStream("F:"
+ "\\eclipsework\\Crazy-java\\src\\com\\Licht\\_15\\ReadFile.java");
FileChannel fcin = fis.getChannel();){
//定义一个ByteBuffer对象,用于重复取水
ByteBuffer buffer = ByteBuffer.allocate(256);
while(fcin.read(buffer) != -1){
buffer.flip();
Charset charset = Charset.forName("GBK");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buffer);
System.out.println(charBuffer);
buffer.clear();
}
}

}
}

字符集和Charset

import java.nio.CharBuffer;s
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

获取Java所支持的全部字符集

1
2
3
4
sortedMap<String, Charset> map = Charset.avaliableCharsets();
for(String alias : map.keySet()){
System.out.println(alias --- >map.get(alias));
}

keySet()方法获取map中所有key值,get(Object key)方法获取相应key值对应的value。
创建字符集对象forName(…)方法。字符集的创建、编解码操作步骤如下(以简体中文为例):
1)创建字符集对象
Charset cn = Charset.forName(“GBK”);
2)char <-> byte, String <-> byte,
a) 只是进行简单的编、解码操作,CharBuffer和ByteBuffer都有方法直接转换
ByteBuffer encode(CharBuffer cb)
CharBuffer decode(ByteBuffer bb)
ByteBuffer encode(String str)
b) 若是进行较复杂的操作
获取字符集对象cn对应的编码器、解码器
CharsetEncoder cnEncoder = cn.newEncoder();
CharsetDecoder cnDecoder = cn.newDecoder();
3)创建CharBuffer对象
CharBuffer cbuff = CharBuffer.allocate(8);
4)将CharBuffer中的字符序列转换成字节序列
ByteBuffer bbuff = cnEncoder.encode(cbuff);
for (int i = 0; I < bbuff.capacity(); i ++){
bbuff.get(i);
}

文件锁

可以有效地阻止多个进程并发修改同一个文件。
在NIO中,FileChannel提供的lock()/tryLock()方法返回文件锁FileLock对象,由FileLock执行文件锁定功能。
1)lock()、lock(long position, long size, boolean shared):当试图锁定某个文件时,如果无法得到文件锁,程序将一直阻塞;
2)tryLock()、tryLock(long position, long size, boolean shared):尝试锁定文件,当获得了文件锁,返回该文件锁,否则返回null。
其中shared参数为true是,表明该锁是一个共享锁,允许多个进程读取文件,但阻止其他进程获得对该文件的排他锁;false为排他锁,它将锁住对该文件的读写,lock()/tryLock()默认为排他锁。
FileChannel channel = new FileOutputStream(“a.txt”).getChannel();
FileLock lock = channel.tryLock(); //使用非阻塞方式对文件进行加锁
Thread.sleep(10000); //程序暂停10s,加锁的文件将阻止其他程序对其进行修改
lock.release(); //释放锁
缺陷:文件锁虽然可以控制并发访问,但对于高并发访问的情形,推荐使用数据库。

Java7的NIO.2

Java7对NIO进行了重大改进:
1)新增java.nio.file包及其子包(对文件IO和文件系统访问支持)。
2)新增java.nio.channels包下以Asynchronous开头的Channel接口和类(基于异步Channel的IO)。

Path、Paths、Files核心API

import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.Files;

为了弥补File类操作文件系统的不足,引入了Path接口,代表一个平台无关的平台路径。
Files、Paths工具类,Files包含大量工具方法,Paths包含两个返回Path的静态工厂方法。
早期File类不足,提供Files和Paths工具类,其中Files是一个操作文件的工具类;Paths返回Path类型
1)获取Path对象:Paths的get()方法
Path path = Paths.get(String first, String…more)
2)使用Java8新增的Stream API列出当前目录下所有文件和目录:
Files.list(Paths.get(“.”)).forEach(path -> System.out.println(path);

使用FileVisitor遍历文件和目录

以前遍历指定目录下的所有文件和子目录需要进行递归,较复杂且灵活性不高。Files类提供了如下方法来遍历文件和子目录:
walkFileTree(Path start, FileVisitor<? super Path> visitor)
FileVisitor代表一个文件访问器。遍历文件和目录会触发FileVisitor中相应的方法。可以通过继承SimpleFileVisitor(FileVisitor的实现类)来实现文件访问器,这需要根据需要选择性地重写(@Override)指定方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.FileVisitResult;
import java.nio.file.attribute.BasicFileAttributes;

/*
*使用Visitor遍历文件和子目录
*/
public class FileVisitorTest{
public static void main(String[] args)
throws Exception{
//遍历F:\eclipsework\Crazy-java\src\com\Licht\_15目录下所有文件和子目录
Files.walkFileTree(Paths.get("F:", "eclipsework"
, "Crazy-java", "src", "com", "Licht", "_15")
, new SimpleFileVisitor<Path>(){ //SimpleFileVisitor是FileVisitor的
//实现类
//访问文件时触发该方法
@Override
public FileVisitResult visitFile(Path file
, BasicFileAttributes attrs) throws IOException{
System.out.println("系统正在访问" + file + "文件");
if (file.endsWith("FileVisitor.java")){ //找到目标文件则亭子遍历
System.out.println("已找到目标文件");
return FileVisitResult.TERMINATE; //中止访问
}
return FileVisitResult.CONTINUE; //继续访问
}
//访问目录之前触发该方法
@Override
public FileVisitResult preVisitDirectory(Path dir
, BasicFileAttributes attrs) throws IOException{
System.out.println("正在访问" + dir + "路径");
return FileVisitResult.CONTINUE;
}
});
}
}

使用WatchService监控文件变化

import java.nio.file.FileSystems;
import java.nio.file.WatchService;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchKey;
import java.nio.file.WatchEvent;

WatchService类的对象是操作系统原生的文件系统监控器。NIO.2的Path类提供如下方法来监听文件系统变化:
1) 获取当前OS平台下的文件系统监控器(文件系统的WatchService对象)
WatchService watchService = FileSystems.getDefault().newWatchService();
2) register(注册)
register(WatchService watcher, WatchEvent.Kind<?>…events)
WatchService代表一个文件系统监听服务。用监听器watcher监听path路径下的events类型的事件。
3) 调用WatchService的如下方法获取被监听目录的文件变化事件
WatchKey poll(…):获取下一个WathKey,如果没有就立即返回null。
WatchKey take():获取下一个WathKey,如果没有就一直等待。
其中WatchKey表示WathService注册的监控对象的token(标记)。
WathcKey pollEvent():检索并删除此watch key的所有未决事件,并返回已检索事件的列表

访问文件属性(Attribute)

在java.nio.file.attribute包下,如
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserDefinedFileAttributeView;等
File类可以访问一些简单的文件属性(attribute,属性;归属),如文件的大小、修改时间、文件是否隐藏、是文件还是目录等。Java7的NIO.2在java.nio.file.attribute包下提供了大量的工具类,这些工具类主要分为两类:
1)***AttributeView:某种文件属性的“视图”,FileAttributeView是其它***AttributeView的父接口;
2)***Attributes:某种文件属性的集合,一般通过***AttributeView对象来获取。
具体常用方法:
(1)identify the required view(创建需要的view):
*** AttributeView ***View = Files.getFileAttributeView(path, ***AttributeView.class);
(2)获取访问***属性的***FileAttributes(先确保能读取文件属性):
***FileAttributes ***Attribs = ***View.readAttributes();
(3)访问***属性(如具体的创建时间、上次修改时间等):
***Attribs.creationTime().toMillis(); //访问文件的创建时间